package com.randb.easyhttp.request;

import com.google.gson.reflect.TypeToken;
import com.randb.easyhttp.cache.model.CacheResult;
import com.randb.easyhttp.callback.CallClazzProxy;
import com.randb.easyhttp.callback.Callback;
import com.randb.easyhttp.callback.CallbackProxy;
import com.randb.easyhttp.func.ApiResultFunc;
import com.randb.easyhttp.func.CacheResultFunc;
import com.randb.easyhttp.func.RetryExceptionFunc;
import com.randb.easyhttp.model.ApiResult;
import com.randb.easyhttp.subscriber.CallbackSubscriber;
import com.randb.easyhttp.utils.RxUtils;

import java.lang.reflect.Type;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.disposables.Disposable;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

/**
 * User: RandBII
 * Date: 2020/5/29
 * Description:
 */
@SuppressWarnings("unchecked")
public class PutRequest extends BaseBodyRequest<PutRequest> {

    public PutRequest(String url) {
        super(url);
    }

    public <T> Observable<T> execute(Class<T> clazz) {
        return execute(new CallClazzProxy<ApiResult<T>, T>(clazz) {
        });
    }

    public <T> Observable<T> execute(Type type) {
        return execute(new CallClazzProxy<ApiResult<T>, T>(type) {
        });
    }

    public <T> Observable<T> execute(CallClazzProxy<ApiResult<T>, T> callProxy) {
        return build().generateRequest()
                .map(new ApiResultFunc(callProxy.getType())) // 处理 ResponseBody 转变为ApiResult<T>
                .compose(isSyncRequest ? RxUtils._main() : RxUtils._io_main()) // 处理同步或者异步问题
                .compose(rxCache.transformer(cacheMode, callProxy.getCallType())) // 处理缓存
                .retryWhen(new RetryExceptionFunc(retryCount, retryDelay, retryIncreaseDelay))  // 处理超时重试
                .compose(new ObservableTransformer() {
                    @Override
                    public ObservableSource apply(Observable upstream) {
                        return upstream.map(new CacheResultFunc<T>());
                    }
                });
    }


    public <T> Disposable execute(Callback<T> callback) {
        return execute(new CallbackProxy<ApiResult<T>, T>(callback) {
        });
    }

    private <T> Disposable execute(CallbackProxy<ApiResult<T>, T> callbackProxy) {
        Observable<CacheResult<T>> observable = build().toObservable(generateRequest(), callbackProxy);

        if (CacheResult.class != callbackProxy.getCallback().getRawType()) {

            return observable.compose(new ObservableTransformer<CacheResult<T>, T>() {
                @Override
                public ObservableSource<T> apply(Observable<CacheResult<T>> upstream) {
                    return upstream.map(new CacheResultFunc<T>());
                }
            }).subscribeWith(new CallbackSubscriber<T>(context, callbackProxy.getCallback()));

        } else {
            return observable.subscribeWith(new CallbackSubscriber<CacheResult<T>>(context, callbackProxy.getCallback()));
        }


    }


    private <T> Observable<CacheResult<T>> toObservable(Observable<ResponseBody> observable,
                                                        CallbackProxy<ApiResult<T>, T> callbackProxy) {
        return observable.map(new ApiResultFunc(callbackProxy != null ?
                callbackProxy.getType()
                : new TypeToken<ResponseBody>() {
        }.getType()))
                .compose(isSyncRequest ? RxUtils._main() : RxUtils._io_main())
                .compose(rxCache.transformer(cacheMode, callbackProxy.getCallback().getType()))
                .retryWhen(new RetryExceptionFunc(retryCount, retryDelay, retryIncreaseDelay));

    }


    @Override
    protected Observable<ResponseBody> generateRequest() {

        if (this.requestBody != null) {
            return apiManager.putBody(url, this.requestBody);
        } else if (this.json != null) {
            RequestBody jsonBody = RequestBody.create(this.json, MediaType.parse("application/json;charset=utf-8"));
            return apiManager.putJson(url, jsonBody);
        } else if (this.str != null) {
            RequestBody strBody = RequestBody.create(this.str, MediaType.parse("text/plain"));
            return apiManager.putBody(url, strBody);
        } else if (this.object != null) {
            return apiManager.putBody(url, object);
        } else {
            return apiManager.put(url, params.urlParamsMap);
        }

    }
}
